home *** CD-ROM | disk | FTP | other *** search
/ Programmer Plus 2007 / Programmer-Plus-2007.iso / Programming / Report Writers / Crystal Repot 9.0 Full CD version / Setup.exe / SRC / HOARDDLL.ZIP / 3rdParty / hoard / libhoard-2.0.2 / log.h < prev    next >
Encoding:
C/C++ Source or Header  |  2000-03-20  |  10.1 KB  |  391 lines

  1. ///-*-C++-*-//////////////////////////////////////////////////////////////////
  2. //
  3. // Hoard: A Fast, Scalable, and Memory-Efficient Allocator
  4. //        for Shared-Memory Multiprocessors
  5. // Contact author: Emery Berger, http://www.cs.utexas.edu/users/emery
  6. //
  7. // Copyright (c) 1998-2000, The University of Texas at Austin.
  8. //
  9. // This library is free software; you can redistribute it and/or modify
  10. // it under the terms of the GNU Library General Public License as
  11. // published by the Free Software Foundation, http://www.fsf.org.
  12. //
  13. // This library is distributed in the hope that it will be useful, but
  14. // WITHOUT ANY WARRANTY; without even the implied warranty of
  15. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  16. // Library General Public License for more details.
  17. //
  18. //////////////////////////////////////////////////////////////////////////////
  19.  
  20. #ifndef _LOG_H_
  21. #define _LOG_H_
  22.  
  23. #include <assert.h>
  24. #include <fcntl.h>
  25. #include <unistd.h>
  26. #include <stdio.h>
  27. #include <string.h>
  28. #include <pthread.h>
  29.  
  30. #include <sys/types.h>
  31. #include <sys/stat.h>
  32. #include <sys/mman.h>
  33.  
  34. #define USE_LOCKS 0
  35.  
  36. #if USE_LOCKS
  37.  
  38. class lock {
  39. public:
  40.   lock (pthread_mutex_t& l)
  41.     : _theLock (l)
  42.   {
  43.     pthread_mutex_lock (&_theLock);
  44.   }
  45.  
  46.   ~lock (void)
  47.   {
  48.     pthread_mutex_unlock (&_theLock);
  49.   }
  50. private:
  51.   pthread_mutex_t& _theLock;
  52. };
  53.  
  54. #else
  55.  
  56. class lock {
  57. public:
  58.   lock (pthread_mutex_t&)
  59.   {}
  60.  
  61.   ~lock (void)
  62.   {}
  63. };
  64.  
  65. #endif // USE_LOCKS
  66.  
  67.  
  68. // A log that can be treated as a dynamic array and a file.
  69.  
  70. template <class TYPE>
  71. class Log {
  72. public:
  73.  
  74.   enum { READ_WRITE = 0,
  75.      READ_ONLY = 1 };
  76.  
  77.   inline Log (void);
  78.   ~Log (void) {
  79.     // Close the logfile if it's still open.
  80.     if (isOpen())
  81.       close();
  82.   }
  83.  
  84.   // Open a (possibly new) log.
  85.   void open (const char * filename,
  86.          int readOnly = READ_WRITE)
  87.   {
  88.     lock m (_lock);
  89.     _readOnlyMode = readOnly;
  90.     unlocked_open (filename);
  91.   }
  92.  
  93.   // Close a log.
  94.   inline void close (void);
  95.  
  96.   // Abort a log. (Don't write any changes.)
  97.   inline void abort (void);
  98.  
  99.   // Clear a log.
  100.   inline void clear (void);
  101.  
  102.   // Access a log entry directly.
  103.   inline TYPE& operator[] (int i);
  104.   inline TYPE operator[] (int i) const;
  105.  
  106.   // Add an entry to the end of the log.
  107.   void append (TYPE& t) {
  108.     assert (!isReadOnly());
  109.     lock m (_lock);
  110.     int len = _perceivedLength;
  111.     adjustArray (len);
  112.     assert (_perceivedLength == len + 1);
  113.     ((TYPE *) ((char *) _start + sizeof(int)))[len] = t;
  114.   }
  115.  
  116.   // Return the number of elements in the log.
  117.   int length (void) {
  118.     lock m (_lock);
  119.     assert (_isOpen);
  120.     int len = _perceivedLength;
  121.     return len;
  122.   }
  123.  
  124. private:
  125.  
  126.   // Open a file and mmap it. This version does not acquire the lock.
  127.   inline void unlocked_open (const char * filename);
  128.  
  129.   // Close the file and related memory maps.
  130.   void closeEverything (void) {
  131.     if (!isReadOnly()) {
  132.       // Save the perceived length.
  133.       *((int *) _start) = _perceivedLength;
  134.       
  135.       // Mmap the file and extend it as needed.
  136.       int fd = ::open (_filename, O_CREAT | O_RDWR, 00600);
  137.       assert (fd != -1);
  138.       lseek (fd, fileLength(_perceivedLength) - 1, SEEK_SET);
  139.       int x = 0;
  140.       write (fd, (void *) &x, 1);
  141.       char * fileStart = (char *) mmap (0, fileLength(_perceivedLength), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
  142.       memcpy (fileStart, _start, fileLength(_perceivedLength));
  143.       // Make sure everything has been written out, then close the file.
  144.       msync (fileStart, fileLength(_perceivedLength), MS_SYNC);
  145.       munmap (fileStart, fileLength(_perceivedLength));
  146.     }
  147.     // Close the memory mapped block as well.
  148.     munmap (_start, fileLength(_actualLength));
  149.     ::close (_memFd);
  150.   }
  151.  
  152.   // Grow the array if necessary to accomodate the given index.
  153.   inline void adjustArray (int index);
  154.  
  155.   int isOpen (void) {
  156.     return _isOpen;
  157.   }
  158.  
  159.   int isReadOnly (void) {
  160.     return (_readOnlyMode == READ_ONLY);
  161.   }
  162.  
  163.   // Given the total number of objects, return the file length.
  164.   int fileLength (int nobjs) {
  165.     return nobjs * sizeof(TYPE) + sizeof(int);
  166.   }
  167.  
  168.   // Given the length of a file, return the total number of objects.
  169.   int numberOfObjects (int fileLen) {
  170.     return ((int) fileLen - sizeof(int)) / sizeof(TYPE);
  171.   }
  172.  
  173.   char _filename[255];    // The log's filename.
  174.   char * _start;    // The start address of the mmapped memory block.
  175.   int  _actualLength;    // The actual length of the file, in # of TYPEs.
  176.   int  _perceivedLength; // The perceived length of the file, in # of TYPEs.
  177.   int _memFd;        // The file descriptor of the mmapped memory block.
  178.   int _isOpen;        // Is the log file open?
  179.   int _readOnlyMode;    // Did we open the file read-only?
  180.   pthread_mutex_t _lock;
  181.  
  182.   double _pad[8];
  183.  
  184. };
  185.  
  186.  
  187. /*
  188.  
  189.   The structure of the log file is as follows:
  190.  
  191.   +------------------+------+------+------+------+------+
  192.   | perceived length | item | item | item | item | item |
  193.   +------------------+------+------+------+------+------+
  194.  
  195.   The logfile implicitly grows to fit as the 'array' is indexed
  196.   (or append is called). We always double the actual size of the
  197.   array so we only do a logarithmic number of array re-sizes.
  198.  
  199. */
  200.  
  201.  
  202. template <class TYPE>
  203. Log<TYPE>::Log (void)
  204.   : _start (NULL),
  205.     _actualLength (0),
  206.     _perceivedLength (0),
  207.     _isOpen (0)
  208. {}
  209.  
  210.  
  211. template <class TYPE>
  212. void Log<TYPE>::unlocked_open (const char * filename)
  213. {
  214.   if (!isOpen()) {
  215.  
  216.     // Save the filename.
  217.     strncpy ((char *) _filename, (char *) filename, 255);
  218.  
  219.     int nobjs;
  220.     if (!isReadOnly()) {
  221.       // Open the file, creating it if necessary.
  222.       int fd = ::open (_filename, O_CREAT | O_RDWR, 00600);
  223.       assert (fd != -1);
  224.       
  225.       // Find out how long the file is.
  226.       struct stat buf;
  227.       fstat (fd, &buf);
  228.       
  229.       // Calculate how many objects are in the file.
  230.       if (buf.st_size == 0) {
  231.     // The file is empty.
  232.     nobjs = 0;
  233.     // Write a zero into the beginning for the perceived length.
  234.     int pl = 0;
  235.     write (fd, (void *) &pl, sizeof(int));
  236.       } else {
  237.     nobjs = numberOfObjects((int) buf.st_size);
  238.     assert (fileLength(nobjs) == (int) buf.st_size);
  239.       }
  240.       
  241.       // Copy the file into memory.
  242.       
  243.       // mmap the file.
  244.       char * fileStart = (char *) mmap (0, fileLength(nobjs), PROT_READ, MAP_PRIVATE | MAP_NORESERVE, fd, 0);
  245.       
  246.       _memFd = ::open ("/dev/zero", O_RDWR);
  247.       _start = (char *) mmap (0, fileLength(nobjs), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_NORESERVE, _memFd, 0);
  248.       memcpy (_start, fileStart, fileLength(nobjs));
  249.       munmap (fileStart, fileLength(nobjs));
  250.       ::close (fd);
  251.  
  252.       _isOpen = 1;
  253.  
  254.     } else {
  255.  
  256.       // Read-only.
  257.  
  258.       // Open the file.
  259.       // The file MUST exist and be non-empty.
  260.       _memFd = ::open (_filename, O_RDONLY, 00600);
  261.       assert (_memFd != -1);
  262.       
  263.       // Find out how long the file is.
  264.       struct stat buf;
  265.       fstat (_memFd, &buf);
  266.       
  267.       // Calculate how many objects are in the file.
  268.       nobjs = numberOfObjects((int) buf.st_size);
  269.       assert (fileLength(nobjs) == (int) buf.st_size);
  270.      
  271.       // mmap the file.
  272.       _start = (char *) mmap (0, fileLength(nobjs), PROT_READ, MAP_PRIVATE | MAP_NORESERVE, _memFd, 0);
  273.  
  274.       _isOpen = 1;
  275.     }
  276.  
  277.  
  278.     // Make sure the mmap worked.
  279.     assert ((int) _start != -1);
  280.     assert (_start != NULL);
  281.  
  282.     // Set the length values.
  283.     _actualLength = nobjs;
  284.     _perceivedLength = *((int *) _start);
  285.  
  286.     // If there are no objects, the perceived length better be zero.
  287.     assert ((nobjs != 0) || (_perceivedLength == 0));
  288.     // Other sanity checks.
  289.     assert (_perceivedLength <= _actualLength);
  290.     assert (_actualLength >= 0);
  291.     assert (_perceivedLength >= 0);
  292.   }
  293. }
  294.  
  295.  
  296. template <class TYPE>
  297. void Log<TYPE>::close (void) {
  298.   lock m (_lock);
  299.   if (isOpen()) {
  300.     assert (_start != NULL);
  301.     closeEverything();
  302.     _isOpen = 0;
  303.     _actualLength = 0;
  304.     _perceivedLength = 0;
  305.   }
  306. }
  307.  
  308.  
  309. template <class TYPE>
  310. void Log<TYPE>::abort (void) {
  311.   lock m (_lock);
  312.   if (isOpen()) {
  313.     assert (_start != NULL);
  314.     munmap (_start, fileLength(_actualLength));
  315.     ::close (_memFd);
  316.     _isOpen = 0;
  317.     _actualLength = 0;
  318.     _perceivedLength = 0;
  319.   }
  320. }
  321.  
  322.  
  323. template <class TYPE>
  324. void Log<TYPE>::clear (void) {
  325.   lock m (_lock);
  326.   if (isOpen()) {
  327.     // Close the file & the mmap.
  328.     closeEverything();
  329.     _isOpen = 0;
  330.     // Now delete it and re-open it.
  331.     unlink (_filename);
  332.     unlocked_open (_filename);
  333.   }
  334. }
  335.  
  336.  
  337. template <class TYPE>
  338. TYPE& Log<TYPE>::operator[] (int i)
  339. {
  340.   lock m (_lock);
  341.   if (!isReadOnly()) {
  342.     // Increase the array length to accomodate index i, if necessary.
  343.     adjustArray (i);
  344.   }
  345.   // Return a reference to the chosen array entry.
  346.   return ((TYPE *) ((char *) _start + sizeof(int)))[i];
  347. }
  348.  
  349.  
  350. template <class TYPE>
  351. void Log<TYPE>::adjustArray (int i)
  352. {
  353.   assert (isOpen());
  354.   assert (!isReadOnly());
  355.   assert (i >= 0);
  356.   // If we try to access an object that is beyond the actual length of
  357.   // the file, we need to extend the file and update the mmap.
  358.   if (i >= _actualLength) {
  359.     int newMemFd = ::open ("/dev/zero", O_RDWR);
  360.     char * newStart = (char *) mmap (0, fileLength(2 * i + 1), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_NORESERVE, newMemFd, 0);
  361.     memcpy (newStart, _start, fileLength(_actualLength));
  362.     ::close (_memFd);
  363.     _start = newStart;
  364.     _memFd = newMemFd;
  365.     _actualLength = 2 * i + 1;
  366.   }
  367.   assert (i < _actualLength);
  368.   // At this point, i definitely fits in the space allocated to it.
  369.   // However, we may need to extend the perceived length.
  370.   if (i >= _perceivedLength) {
  371.     _perceivedLength = i + 1;
  372.   }
  373.   assert (i < _perceivedLength);
  374. }
  375.  
  376.  
  377. template <class TYPE>
  378. TYPE Log<TYPE>::operator[] (int i) const
  379. {
  380.   // Here we do NOT update the array size.  The index should be within
  381.   // the perceived size of the array.
  382.   lock m (_lock);
  383.   assert (isOpen());
  384.   assert (i >= 0);
  385.   assert (i < _perceivedLength);
  386.   return ((TYPE *) ((char *) _start + sizeof(int)))[i];
  387. }
  388.  
  389.  
  390. #endif // _LOG_H_
  391.